package com.blackcat.atlas;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.blackcat.atlas.controller.TreeNode;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

/**
 * 描述 ：
 *
 * @author : zhangdahui
 * @date : 2022/1/24 14:14
 */
@Service
public class AtlasAPIService {

    private String atlasIPPort = "39.105.55.147:21000";
    private String userPwd = "admin:admin";
    private String columnGuid = "9c5e01f4-6891-4770-b0e5-b485e479884b";
//    private String columnGuid = "c66f0675-81b8-4bce-b44b-53ab47d72405";
//    private String columnGuid = "e859ef05-0b22-4c3c-b713-ff5091a2306f";
    private String tableGuid = "3d40f9e7-0a5b-4904-96f1-14d39457dc25";
    private String processGuid = "f62f3589-b05d-4c80-95ba-65ae343d330b";

    Map<String, Object> lineageData;

    public Map<String,Object> dataConvert(){
        Map<String, Object> result = new HashMap<>();
        // 详情数据
        Map<String, Object> nodeMap = new HashMap<>();
        // 关系数据
        List<Relationship> relations = new ArrayList<>();
        // 第一步：根据字段唯一标识获取血缘数据
        lineageData = apiLineageGuid(atlasIPPort, userPwd, columnGuid);
        Map<String, Object> guidEntityMap = (Map<String, Object>) lineageData.get("guidEntityMap");
        // 第二步：根据唯一标识找到封装对应详细信息
        guidEntityMap.forEach((guid,info)->{
            Map m = (Map) info;
            String typeName = m.get("typeName").toString();
            // 根据唯一标识调用查询详情方法
            Map entityResult = apiEntityGuid(atlasIPPort, userPwd, guid);
            Map entity = (Map) entityResult.get("entity");
            if(AppConst.TYPE_NAME_TABLE.equals(typeName)){
                // 根据表唯一标识，查询所有字段、表信息
                if (nodeMap.get(guid) == null) {// 相同表字段，不需要多个节点
                    nodeMap.put(guid, getTableInfoByTableId(guid));
                }
            }else if (AppConst.TYPE_NAME_PROCESS.equals(typeName)){
                // 根据过程唯一标识查询过程详情。
                if (nodeMap.get(guid) == null) {// 相同过程，不需要多个节点
                    nodeMap.put(guid, getProcessByGuid(guid));
                }
            }else if (AppConst.TYPE_NAME_COLUMN.equals(typeName)){
                // 根据血缘数据中字段，找到所属表唯一标识
                Map relationshipAttributes = (Map) entity.get("relationshipAttributes");
                Map table = (Map) relationshipAttributes.get("table");
                String tableId = table.get("guid").toString();
                // 根据表唯一标识，查询所有字段、表信息
                if (nodeMap.get(tableId) == null) {// 相同表字段，不需要多个节点
                    nodeMap.put(tableId, getTableInfoByTableId(tableId));
                }
            }else if (AppConst.TYPE_NAME_COLUMN_LINEAGE.equals(typeName)){
                // 根据过程唯一标识，查询字段血缘中过程信息，得到过程唯一标识。
                Map attributes = getAttributes(entity);
                Map process = (Map) attributes.get("query");
                String processGuid = process.get("guid").toString();
                // 根据过程唯一标识查询过程详情。
                if (nodeMap.get(processGuid) == null) {// 相同过程，不需要多个节点
                    nodeMap.put(processGuid, getProcessByGuid(processGuid));
                }
            }
        });
        // 第四步：处理关系
        List<Map> relationList = (List<Map>) lineageData.get("relations");
        List<TreeNode> listRight = new ArrayList<>();
        List<TreeNode> listLeft = new ArrayList<>();
        relationList.forEach(relation->{
            String fromEntityId = relation.get("fromEntityId").toString();
            String toEntityId = relation.get("toEntityId").toString();
            listRight.add(new TreeNode(toEntityId, fromEntityId));
            listLeft.add(new TreeNode(fromEntityId, toEntityId));
            relations.add(new Relationship(toEntityId,fromEntityId));
        });
        // 影响
        TreeNode treeRight = new TreeNode(columnGuid,null);
        findChildren(treeRight, listRight);
//        System.out.println(JSONArray.toJSON(treeRight));
        // 溯源
        TreeNode treeLeft = new TreeNode(columnGuid,null);
        findChildren(treeLeft, listLeft);
//        System.out.println(JSONArray.toJSON(treeLeft));

        // 第四步：坐标计算
        // 第五步：数据封装

        result.put("nodeMap", nodeMap);
        result.put("relations", relations);
        System.out.println("result:"+JSONObject.toJSON(result));
        return result;
    }

    public Map apiSearchDsl(){
        String dsl = "from%20hive_table%20where%20db.name%20=%20%22test_bk%22%20and%20table.name=%22test_bk_e_1%22%20and%20__state%20=%20%22ACTIVE%22%0D%0Aselect%20columns";
        String[] url = {"curl",
                "-u", userPwd,
                "-X", "GET",
                "http://"+atlasIPPort+"/api/atlas/v2/search/dsl?limit=100&offset=0&query="+dsl,
                "-H", "accept: */*", "-H", "Content-Type: application/json;charset=UTF-8"
        };
        String json = execCurl(url);
        return JSONObject.parseObject(json);
    }

    /**
     * 描述 : 根据过程唯一标识查询过程详情
     * @author : zhangdahui
     * @date : 2022/2/10 14:17
     * @param guid 过程唯一标识
    */
    private Node getProcessByGuid(String guid){
        Node node = new Node();
        Map entityResult = apiEntityGuid(atlasIPPort, userPwd, guid);
        Map entityAttributes = getEntityAttributes(entityResult);
        node.setName(entityAttributes.get("name").toString());
        node.setSql(entityAttributes.get("recentQueries").toString());
//        List<Map<String, Object>> outputs = (List<Map<String, Object>>) entityAttributes.get("outputs");
//        outputs.forEach(map->{
//            map.get("guid");
////            map.get("typeName");
//        });
//        List<Map<String, Object>> inputs = (List<Map<String, Object>>) entityAttributes.get("inputs");
        return node;
    }

    /**
     * 描述 : 根据节点查询所有子节点
     * @author : zhangdahui
     * @date : 2022/2/9 9:24
     * @param tree 查询节点
     * @param list 所有节点
    */
    private static TreeNode findChildren(TreeNode tree, List<TreeNode> list) {
        for (TreeNode node : list) {
            if (node.getParentId().equals(tree.getId())) {
                if (tree.getChildren() == null) {
                    tree.setChildren(new ArrayList<>());
                }
                tree.getChildren().add(findChildren(node, list));
            }
        }
        return tree;
    }

//    /**
//     * 描述 : 根据字段id获取表id
//     * @author : zhangdahui
//     * @date : 2022/1/26 14:36
//     * @param guid 唯一标识
//    */
//    private String getTableIdByColumnGuid(String guid){
//        Map column = apiEntityGuid(atlasIPPort, userPwd, guid);
//        Map entity = (Map) column.get("entity");
//        Map relationshipAttributes = (Map) entity.get("relationshipAttributes");
//        if (relationshipAttributes.get("table") != null) {
//            Map table = (Map) relationshipAttributes.get("table");
//            return table.get("guid").toString();
//        }
//        return null;
//    }

    /**
     * 描述 : 获取血缘数据
     * @author : zhangdahui
     * @date : 2022/1/24 15:43
     * @param guid 唯一标识
    */
    private Map<String,Object> apiLineageGuid(String ipPort,String userPwd,String guid){
        String[] cmds = {"curl",
                "-u", userPwd,
                "-X", "GET",
                "http:/"+ipPort+"/api/atlas/v2/lineage/"+guid,
                "-H", "accept: */*", "-H", "Content-Type: application/json;charset=UTF-8"
                };
        return JSONObject.parseObject(execCurl(cmds));
    }

    /**
     * 描述 : 获取表数据
     * @author : zhangdahui
     * @date : 2022/1/24 15:43
     * @param guid 唯一标识
    */
    private Node getTableInfoByTableId(String guid){
        Node node = new Node();
        node.setGuid(guid);
        Map tableInfo = apiEntityGuid(atlasIPPort,userPwd,guid);
        // 获取表名
        Map entity = (Map) tableInfo.get("entity");
        Map table=getAttributes(entity);
        node.setName(table.get("name").toString());
        // 获取表所属数据库
        Map relationshipAttributes = (Map) entity.get("relationshipAttributes");
        Map db = (Map) relationshipAttributes.get("db");
        Map dbInfo = apiEntityGuid(atlasIPPort,userPwd,db.get("guid").toString());
        Map dbAttributes = getEntityAttributes(dbInfo);
        node.setDb(dbAttributes.get("name").toString());
        // 获取表所属schema TODO hive不需要
        relationshipAttributes.get("schema");
        // 获取字段数据
        Map entities = (Map) tableInfo.get("referredEntities");
        List<Column> columns = new ArrayList<>();
        entities.forEach((k,v)->{
            Column info = new Column();
            Map vMap = (Map) v;
            Map attributes = getAttributes(vMap);
            if (attributes.get("name") != null) {
                info.setGuid(k.toString());
                info.setName(attributes.get("name").toString());
                info.setType(attributes.get("type").toString());
                info.setPosition((Integer) attributes.get("position"));
                columns.add(info);
            }
        });
        // 字段排序
        Collections.sort(columns, (o1, o2) -> o1.getPosition()<o2.getPosition()? -1:(o1.getPosition().equals(o2.getPosition()) ? 0:1));
        node.setChildren(columns);
        return node;
    }

    /**
     * 描述 : api接口
     * @author : zhangdahui
     * @date : 2022/1/26 10:04
     * @param guid 唯一标识
    */
    private Map apiEntityGuid(String ipPort,String userPwd,String guid){
        String[] url = {"curl",
                "-u", userPwd,
                "-X", "GET",
                "http://"+ipPort+"/api/atlas/v2/entity/guid/"+guid,
                "-H", "accept: */*", "-H", "Content-Type: application/json;charset=UTF-8"
        };
        String json = execCurl(url);
        return JSONObject.parseObject(json);
    }

    /**
     * 描述 : 获取map中attributes
     * @author : zhangdahui
     * @date : 2022/1/24 16:53
     * @param map 数据
    */
    private Map getAttributes(Map map){
        Map attributes = (Map) map.get("attributes");
        return attributes;
    }

    /**
     * 描述 : 获取map中entity中attributes
     * @author : zhangdahui
     * @date : 2022/1/24 16:53
     * @param map 数据
     */
    private Map getEntityAttributes(Map map){
        Map entity = (Map) map.get("entity");
        Map attributes = (Map) entity.get("attributes");
        return attributes;
    }

    /**
     * 描述 : 代码执行curl命令
     * @author : zhangdahui
     * @date : 2022/1/24 14:33 
     * @param cmds 命令参数
    */
    private static String execCurl(String[] cmds) {
        ProcessBuilder process = new ProcessBuilder(cmds);
        Process p;
        try {
            p = process.start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
            StringBuilder builder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                builder.append(line);
                builder.append(System.getProperty("line.separator"));
            }
            return builder.toString();
        } catch (IOException e) {
            System.out.print("error");
            e.printStackTrace();
        }
        return null;
    }
}
